package com.ldodds.deville.calais;

import java.io.IOException;
import java.io.InputStream;
import java.io.StringBufferInputStream;
import java.io.StringReader;

import org.w3c.dom.*;
import org.xml.sax.SAXException;

import javax.xml.parsers.*;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.PostMethod;

import com.hp.hpl.jena.util.FileManager;

public class OpenCalaisImpl implements OpenCalais
{
	private static final String LICENCE_ID = "licenseID";
	private static final String CONTENT = "content";
	private static final String PARAMS_XML = "paramsXML";

	private String licenceKey;
	
	public OpenCalaisImpl() {		
	}
	
	public OpenCalaisImpl(String licenceKey) {
		this.licenceKey = licenceKey;
	}
	
	public String getLicenceKey() {
		return licenceKey;
	}

	public void getLicenceKey(String licenceKey) {
		this.licenceKey = licenceKey;
	}

	public String enlighten(String content) 
	throws IOException, CalaisException {
		return this.enlighten(content, new ParametersImpl() );
	}
		
	public String enlighten(String content, Parameters parameters) 
		throws IOException, CalaisException {
		
		PostMethod method = null;
		String response = null;
		try {
			HttpClient client = new HttpClient();
			method = new PostMethod( getCalaisURI() );
			method.addParameter(LICENCE_ID, licenceKey);
			method.addParameter(CONTENT, content);			
			method.addParameter(PARAMS_XML, getParamsXML(parameters) );

			int statusCode = client.executeMethod( method );			

			//following will thrown an exception if the 
			//response included an error message
			response = 
				parseCalaisResponse(statusCode, method.getResponseBodyAsString() );
			
		} finally {
			if (method != null) { method.releaseConnection(); }
		}
		return response;
	}

	public String enlighten(InputStream in) 
	throws IOException, CalaisException {
		return this.enlighten(in, new ParametersImpl() );
	}

	
	public String enlighten(InputStream in, Parameters parameters) 
		throws IOException, CalaisException {
		String all = FileManager.get().readWholeFileAsUTF8(in);
		return enlighten(all);
	}

	
	/**
	 * Removes the "string" tag wrapping the escaped XML returned 
	 * by the web service. The result will be a chunk of RDF/XML 
	 * suitable for parsing and further manipulation.
	 * @return a String containing RDF/XML from the service.
	 */
	protected String parseCalaisResponse(int status, String responseBody) 
		throws IOException, CalaisException {

		try {
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			factory.setValidating(false);
			factory.setNamespaceAware(true);
			DocumentBuilder builder = factory.newDocumentBuilder();
			Document document = 
				builder.parse( new StringBufferInputStream(responseBody) );
			document.normalize();
			Element root = document.getDocumentElement();
			
			//if the response is an error, throw an exception
			if ( root.getLocalName().equals("Error") ) {
				NodeList list = root.getElementsByTagName("Exception");
				if ( list.getLength() != 0 ) {
					Element errorTextNode = (Element)list.item(0);			
					throw new CalaisException(status, 
							"Calais signalled error", errorTextNode.getTextContent() );
					
				}
			}
			
			//if we've got the response wrapped in the expected string
			//element then return the text
			if (root.getNamespaceURI() != null && 
					root.getNamespaceURI().equals("http://clearforest.com/") &&
					root.getLocalName().equals("string")) {
				Text rdf = (Text) root.getFirstChild();
				return rdf.getTextContent();				
			} 
			
			throw new CalaisException(status, "Unexpected response format", 
						responseBody);
			
		} catch (ParserConfigurationException e) {
			throw new RuntimeException(e);
		} catch (SAXException se) {
			//FIXME
			throw new RuntimeException(se);
		}
	}
	
	protected String getCalaisURI() {
		return CALAIS_URI;
	}
	
	protected String getParamsXML(Parameters parameters) {
		return new ParametersXMLBuilderImpl().buildXML(parameters);
	}
	
	
}
